;; (C) 2007 Vikas Gorur <vikas@80x25.org>

;; TRAT (Tail Recursive Acronym: TRAT) is a music player for Emacs
;; that uses xmms2 as its backend
;;
;; Trat is also the capital of the Trat province in Thailand.

;;     This program is free software; you can redistribute it and/or
;;     modify it under the terms of the GNU General Public License as
;;     published by the Free Software Foundation; either version 3 of
;;     the License, or (at your option) any later version.
    
;;     This program is distributed in the hope that it will be useful,
;;     but WITHOUT ANY WARRANTY; without even the implied warranty of
;;     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;     GNU General Public License for more details.
    
;;     You should have received a copy of the GNU General Public
;;     License along with this program; if not, write to the Free
;;     Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;;     Boston, MA 02110-1301 USA

(defvar trat-playlist nil
  "The trat playlist, containing filenames")

(defvar trat-now-playing nil
  "The song that is being played now")

(defun trat-callback (proc condition)
  (when (string= condition "finished\n")
    (setq trat-now-playing nil)
    (when (not (endp trat-playlist))
      (trat-play-song (car trat-playlist))
      (setq trat-playlist (cdr trat-playlist)))
    (trat-update-buffer)))

(defun trat-play-song (path)
  (call-process "xmms2" nil nil nil "clear")
  (call-process "xmms2" nil nil nil "add" 
		(if (string= "http" (substring path 0 4))
		    path
		  (expand-file-name path)))
  (setq trat-now-playing path)
  (call-process "xmms2" nil nil nil "play"))

(defun trat-play (path)
  "Empties the playlist and plays the song given as argument"
  (interactive "FFile to play: ")
  (setq trat-playlist nil)
  (trat-play-song path)
  (trat-update-buffer))

(defun trat-play-url (url)
  "Same as tray-play except completion is not attempted"
  (interactive "sURL to play: ")
  (trat-play url))
   
(defun trat-enqueue (path)
  "Enqueues the song in the playlist"
  (interactive "fFile to enqueue: ")
  (if (get-process "mplayer")
      (setq trat-playlist (append trat-playlist (list path)))
    (trat-play path))
  (trat-update-buffer))

(defun trat-enqueue-url (url)
  "Same as tray-enqueue except completion is not attempted"
  (interactive "sURL to enqueue: ")
  (trat-enqueue url))

(defun trat-delete ()
  "Delete a file from the playlist"
  (interactive)
  (let ((path (completing-read "File to delete: "
			       (mapcar 'list trat-playlist) nil t)))
    (if (not (string= path ""))
	(setq trat-playlist (remove-if (lambda (key)
					   (string= key path))
					 trat-playlist))))
  (trat-update-buffer))

(defun trat-show-lyrics (artist song)
  "Fetches lyrics and displays in w3m"
  (interactive "sArtist: \nsSong title: ")
  (w3m-goto-url-new-session
   (format "http://lyrc.com.ar/tema1en.php?songname=%s&artist=%s"
	   song artist)))

(defun trat-next ()
  "Skip the current song and play next song in playlist"
  (interactive)
  (when (get-process "mplayer")
      (send-string "mplayer" "q"))
  (trat-update-buffer))

(defvar trat-paused nil
  "True if playback has been paused")
  
(defun trat-pause ()
  "Pause or unpause trat"
  (interactive)
  (when (get-process "mplayer")
    (send-string "mplayer" " ")
    (setq trat-paused (not trat-paused))
    (trat-update-buffer)))

(defun trat-stop ()
  "Stop playing, killing the process"
  (interactive)
  (call-process "xmms2" nil nil nil "stop"))

(defun trat-now-playing ()
  (interactive)
  (message trat-now-playing))

(defun trat-update-buffer ()
  (let ((buf (get-buffer-create "*trat*")))
    (set-buffer buf)
    (let ((buffer-read-only nil)
	  (title "Tail Recursive Acronym: TRAT"))
      (erase-buffer)
      
      (add-text-properties 0 (length title)
			   '(face (:weight bold :foreground "yellow"))
			   title)
      (insert title)
      (center-line) (insert "\n")

      (insert "Volume: ") 
      (insert (with-output-to-string (princ *trat-volume* (get-buffer "*trat*"))))
      (insert "/100\n")

      (let ((now-play "\nNow playing: "))
	(add-text-properties 0 (length now-play)
			     '(face (:weight bold :foreground "blue"))
			     now-play)
	(insert now-play))
      
      (insert (if trat-paused "[PAUSED]" "") "\n  "
	      (if trat-now-playing
		  trat-now-playing
		""))

      (let ((pl "\n\nPlaylist: \n"))
	(add-text-properties 0 (length pl)
			     '(face (:weight bold :foreground "blue"))
			     pl)
	(insert pl))
      
      (mapcar (lambda (path)
		(insert "  " path "\n"))
	      trat-playlist))
    (setq buffer-read-only t)))

(defvar *trat-volume* 24)

(defun trat-decrease-volume ()
  (interactive)
  (when (> *trat-volume* 0)
    (setq *trat-volume* (- *trat-volume* 4))
    (call-process "xmms2" nil nil nil "volume" 
		  (with-output-to-string (princ *trat-volume*))))
  (trat-update-buffer))

(defun trat-increase-volume ()
  (interactive)
  (when (< *trat-volume* 100)
    (setq *trat-volume* (+ 4 *trat-volume*))
    (call-process "xmms2" nil nil nil "volume" 
		  (with-output-to-string (princ *trat-volume*))))
  (trat-update-buffer))

(defun trat-quit ()
  (interactive)
  (when (get-process "mplayer")
      (kill-process "mplayer"))
  (kill-buffer (get-buffer "*trat*"))
  (setq trat-now-playing nil
	trat-playlist nil
	trat-paused nil))

(defvar trat-mode-map nil
  "Keymap for the trat major mode")

(progn
  (setq trat-mode-map (make-sparse-keymap))
  (define-key trat-mode-map "p" 'trat-play)
  (define-key trat-mode-map "\M-p" 'trat-play-url)
  (define-key trat-mode-map "e" 'trat-enqueue)
  (define-key trat-mode-map "\M-e" 'trat-enqueue-url)
  (define-key trat-mode-map "d" 'trat-delete)
  (define-key trat-mode-map "l" 'trat-show-lyrics)
  (define-key trat-mode-map "n" 'trat-next)
  (define-key trat-mode-map "u" 'trat-pause)
  (define-key trat-mode-map "s" 'trat-stop)
  (define-key trat-mode-map "-" 'trat-decrease-volume)
  (define-key trat-mode-map "+" 'trat-increase-volume)
  (define-key trat-mode-map "q" 'trat-quit))
	
(defun trat-mode ()
  "Tail Recursive Acronym: TRAT is a music player that runs inside Emacs,
using mplayer to do the actual playing.

The following commands work in the *trat* buffer:

key		binding
---		-------

p		Play a file after clearing the playlist (trat-play)
M-p		Play a URL (trat-play-url)
e		Enqueue a file (trat-enqueue)
M-e		Enqueue a URL (trat-enqueue-url)
d		Delete a file from the playlist
l		Ask for artist and song and attempt to show lyrics for
		it (using w3m) (trat-show-lyrics)
n		Skip the current song (trat-next)
u		Pause or unpause (trat-pause)
s		Stop playing (trat-stop)
-               Decrease volume
+		Increase volume
q		Quit TRAT"
  
  (interactive)
  (kill-all-local-variables)
  (setq major-mode 'trat-mode
	mode-name "TRAT")
  (use-local-map trat-mode-map))

(defun trat-show-buffer ()
  (interactive)
  (trat-update-buffer)
  (switch-to-buffer (get-buffer "*trat*"))
  (trat-mode))

(defun trat ()
  (interactive)
  (trat-show-buffer))